package com.yonyou.pmclouds.procedure.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.conf.PMCloudServerProperties;
import com.yonyou.pmclouds.basecom.consts.EnableStatusConst;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CodeNameUtils;
import com.yonyou.pmclouds.basecom.util.ExcelUtil;
import com.yonyou.pmclouds.basecom.util.HttpTookit;
import com.yonyou.pmclouds.basecom.util.operation.EncryptUtil;
import com.yonyou.pmclouds.industryandtype.rmiitf.IndustryAndTypeQuery;
import com.yonyou.pmclouds.industryandtype.rmiitf.TypeDocQuery;
import com.yonyou.pmclouds.operation.IndustryAndTypeRefVO;
import com.yonyou.pmclouds.procedure.entity.ProcedureVO;
import com.yonyou.pmclouds.procedure.model.ProcedureManager;
import com.yonyou.pmclouds.procedure.rmiitf.ProcedureMaintain;
import com.yonyou.pmclouds.procedure.rmiitf.ProcedureQuery;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

@RequestMapping("/procedure")
@RestController
public class ProcedureController {
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProcedureQuery query;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProcedureMaintain maintain;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IndustryAndTypeQuery industryAndTypeQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TypeDocQuery typeDocQuery;
    @Autowired
    private PMCloudServerProperties pmcloudServerProperties;
    @Autowired
    private ProcedureManager procedureManager;

    /**
     * 工序引用结果
     */
    public static final int STATUS_NOT_REF = 0;
    public static final int STATUS_REF_BY_CHILD = 1;
    public static final int STATUS_REF_BY_QUALITY = 2;
    public static final int STATUS_REF_BY_BILL = 3;

    @GetMapping("/list")
    public ProcedureVO[] getTenantProcedures(@RequestParam(value = "status", required = false) Integer status) throws BusinessException {
        // status, 1:查询启用，2:查询所有(启用+停用)
        byte enableStatus = EnableStatusConst.ENABLE;
        if (status != null)
            enableStatus = status.byteValue();
        ProcedureVO[] allVOs = query.queryProcedureByTenant(RuntimeEnvironment.getTenantId(), enableStatus);

        return buildResultTree(allVOs, StringUtils.EMPTY);
    }

    @PostMapping("/insert")
    public ProcedureVO insert(@RequestBody @Validated(ProcedureVO.InsertValid.class) ProcedureVO procedureVO,
                              BindingResult errors) throws BusinessException {
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        procedureVO.setSname(StringUtils.trim(procedureVO.getSname()));
        // 校验名称和编码的合法性
        if (CodeNameUtils.checkCodeStr(procedureVO.getScode()) >= 0 || CodeNameUtils.checkNameStr(procedureVO.getSname(), null) >= 0) {
            throw new IllegalRequestArgException();
        }
        procedureVO.setPkTenant(RuntimeEnvironment.getTenantId());
        procedureVO.setEnableStatus(EnableStatusConst.ENABLE);
        String id = maintain.insert(procedureVO);
        ProcedureVO vo = query.getById(id);
        return buildPcode(vo);
    }

    @PostMapping("/update")
    public ProcedureVO update(@RequestBody @Validated(ProcedureVO.UpdateValid.class) ProcedureVO procedureVO,
                              BindingResult errors) throws BusinessException {
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        procedureVO.setSname(StringUtils.trim(procedureVO.getSname()));
        if (CodeNameUtils.checkNameStr(procedureVO.getSname(), null) >= 0) {
            throw new IllegalRequestArgException();
        }
        String id = maintain.update(procedureVO);
        ProcedureVO vo = query.getById(id);
        return buildPcode(vo);
    }

    @PostMapping("/delete")
    public ProcedureVO delete(@RequestBody ProcedureVO procedureVO) throws BusinessException {
        if (StringUtils.isEmpty(procedureVO.getPkProcedure()) || StringUtils.isEmpty(procedureVO.getChangeFlag())) {
            throw new IllegalRequestArgException();
        }
        String id = maintain.delete(procedureVO.getPkProcedure(), procedureVO.getChangeFlag());
        return procedureVO;
    }

    /**
     * 查看所有的行业和类型，返回行业和类型的组合型数据实体（类型作为子表）
     * @return
     * @throws BusinessException
     */
    @GetMapping("/queryIndustryAndType")
    public IndustryAndTypeRefVO[] queryIndustryAndType(@RequestParam("billType") String billType) throws BusinessException {
        if (StringUtils.isEmpty(billType)) {
            throw new BusinessException("billType不能为空");
        }
        Map<String, String> params = new HashMap<>();
        params.put("billType", billType);
        String data = "pmCloud_" + System.currentTimeMillis();
        try {
            params.put("key", encryptData(data));
        } catch (Exception e) {
            throw new BusinessException("参数加密失败");
        }
        String result = HttpTookit.doGet(pmcloudServerProperties.getYwaddress() + "/data/opr/industry/queryIndustryAndType", params);
        if (StringUtils.isEmpty(result)) {
            return new IndustryAndTypeRefVO[0];
        }
        JSONObject json = JSONObject.parseObject(result);
        String jsonString = json.getString("result");

        List<IndustryAndTypeRefVO> list  = JSONArray.parseObject(jsonString, new TypeReference<ArrayList<IndustryAndTypeRefVO>>() {});
        if (list == null)
            return new IndustryAndTypeRefVO[0];
        return list.toArray(new IndustryAndTypeRefVO[]{});
    }

    /**
     * 根据选中的类型，预览类型对应的工序模板
     * @param pkType
     * @return
     * @throws BusinessException
     */
    @GetMapping("/queryPreviewProcedures")
    public com.yonyou.pmclouds.operation.ProcedureVO[] queryPreviewProcedures(@RequestParam("pkType") String pkType) throws BusinessException {
        //查询关联类型和工序（一级）的类型档案实体
        if (StringUtils.isEmpty(pkType)) {
            return new com.yonyou.pmclouds.operation.ProcedureVO[0];
        }
        Map<String, String> params = new HashMap<>();
        params.put(new String("pkType[]"),pkType);
        String data = "pmCloud_" + System.currentTimeMillis();
        try {
            params.put("key", encryptData(data));
        } catch (Exception e) {
            throw new BusinessException("参数加密失败");
        }
        String result = HttpTookit.doGet(pmcloudServerProperties.getYwaddress() + "/data/opr/procedure/queryPreviewProcedures", params);
        if (StringUtils.isEmpty(result)) {
            return new com.yonyou.pmclouds.operation.ProcedureVO[0];
        }
        JSONObject json = JSONObject.parseObject(result);
        String jsonString = json.getString("result");
        List<com.yonyou.pmclouds.operation.ProcedureVO> list = JSONArray.parseObject(jsonString, new TypeReference<ArrayList<com.yonyou.pmclouds.operation.ProcedureVO>>() {
        });
        return list.toArray(new com.yonyou.pmclouds.operation.ProcedureVO[]{});
    }

    /**
     * 根据用户选中的类型（可多选），给当前租户，导入类型对应的工序模板（保留租户原有的工序，增量的导入）。
     * 导入时候，判断当前租户已有工序的编码和名字，与导入的工序的编码和名字是否有相同，
     * 如果存在相同，抛异常并把相同的编码或者名字返回。对工序模板的树高，引用关系，以及树结构不进行校验。
     * 默认工序模板中不存在编码名字冲突，并且树结构正确（在导入工序模板时进行校验）。
     * @param pkTypes
     * @return
     * @throws BusinessException
     */
    @PostMapping("/insertProceduresFromPreset")
    public String[] insertProceduresFromPreset(@RequestBody String[] pkTypes) throws BusinessException {
        if(ArrayUtils.isEmpty(pkTypes))
            return new String[0];

        Map<String, String> params = new IdentityHashMap<>();
        for(String pkType : pkTypes)
        {
            params.put(new String("pkType[]"),pkType);
        }
        String data = "pmCloud_" + System.currentTimeMillis();
        try {
            params.put("key", encryptData(data));
        } catch (Exception e) {
            throw new BusinessException("参数加密失败");
        }

        String result =  HttpTookit.doGet(pmcloudServerProperties.getYwaddress()+"/data/opr/procedure/queryPreviewProcedures",params);
        JSONObject json = JSONObject.parseObject(result);
        String jsonString = json.getString("result");


        List<ProcedureVO> list  = JSONArray.parseObject(jsonString, new TypeReference<ArrayList<ProcedureVO>>() {});
        if (list != null) {
            for (ProcedureVO vo : list) {
                // 设置启用状态
                vo.setEnableStatus(EnableStatusConst.ENABLE);
            }
        }

        String[] ids = maintain.insertPresetProcedures(list);
        return ids == null ? new String[0] : ids;
    }

    /**
     * 从Excel中导入工序
     * @param file Excel文件
     * @return 所有被插入的工序的id数组
     */
    @PostMapping("/importProcedureFromExcel")
    public String[] importProcedureFromExcel(@RequestParam(value="file") MultipartFile file) throws BusinessException {
        try{
            List<List<String>> excelData = ExcelUtil.readExcel(file.getInputStream(), 4);
            List<ProcedureVO> procedureVOList = procedureManager.generateProcedureFromExcelData(excelData);
            if (CollectionUtils.isEmpty(procedureVOList)) {
                throw new BusinessException("解析工序导入Excel文件出错");
            }
            String[] ids = maintain.insertExcelProcedures(procedureVOList);
            return ids == null ? new String[0] : ids;
        } catch (Exception e){
            if (e instanceof BusinessException){
                throw (BusinessException)e;
            } else {
                throw new BusinessException("工序导入出错", e);
            }
        }
    }

    @PostMapping("/enable")
    public ProcedureVO enableProcedure(@RequestBody ProcedureVO procedureVO) throws BusinessException {
        if (procedureVO == null)
            throw new BusinessException("传参错误");

        ProcedureVO result = maintain.enable(procedureVO);
        return result;
    }

    @PostMapping("/disable")
    public ProcedureVO disableProcedure(@RequestBody ProcedureVO procedureVO) throws BusinessException {
        if (procedureVO == null)
            throw new BusinessException("传参错误");

        ProcedureVO resultVO = maintain.disable(procedureVO);
        return resultVO;
    }

    @GetMapping("/referCheck")
    public boolean referenceCheck(@RequestParam("pkProcedure") String pkProcedure) throws BusinessException {

        return maintain.referenceCheck(pkProcedure);

        /*if (status == STATUS_REF_BY_CHILD) {
            throw new BusinessException("当前工序已经存在下级工序，不可修改！");
        }
        if (status == STATUS_REF_BY_QUALITY) {
            throw new BusinessException("当前工序已经被检查标准引用，不可修改！");
        }
        if (status != STATUS_NOT_REF) {
            throw new BusinessException("当前工序已经被引用，不可修改！");
        }*/
    }

    /**
     * 将工序构造树返回（pkParent为空返回所有工序，否则返回pkParent下的工序）
     *
     * @param allVOs
     * @param pkParent 工序
     * @return
     */
    private ProcedureVO[] buildResultTree(ProcedureVO[] allVOs, String pkParent) {
        if (ArrayUtils.isEmpty(allVOs)) {
            return allVOs;
        }
        // 构造树
        Map<String, List<ProcedureVO>> procedureChildMap = new HashMap<>();
        Map<String, String> procedureCodeMap = new HashMap<>();//主键编码映射
        for (ProcedureVO vo : allVOs) {
            procedureCodeMap.put(vo.getPkProcedure(), vo.getScode());
        }
        for (ProcedureVO vo : allVOs) {
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            } else {
                //前端需要子节点中存放父节点的编码
                vo.setPcode(procedureCodeMap.get(parent));
            }
            List<ProcedureVO> children = procedureChildMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                procedureChildMap.put(parent, children);
            }
            children.add(vo);
        }
        for (ProcedureVO vo : allVOs) {
            List<ProcedureVO> children = procedureChildMap.get(vo.getPkProcedure());
            if (children != null) {
                vo.setChildProcedure(children.toArray(new ProcedureVO[children.size()]));
            }
        }
        List<ProcedureVO> result = procedureChildMap.get(StringUtils.EMPTY);
        if (result == null || result.size() == 0) {
            result = procedureChildMap.get(pkParent);
        }

        if (result == null) {
            return new ProcedureVO[0];
        } else {
            return result.toArray(new ProcedureVO[result.size()]);
        }
    }

    /**
     * 类内部使用方法，（批量的）根据根节点，查询所有工序节点，构造成树，返回所有树的根节点
     * @param pkRoots
     * @return
     * @throws BusinessException
     */
    private ProcedureVO[] getProceduresByRoots(String[] pkRoots) throws BusinessException {
        //根据根节点主键，查询工序节点
        ProcedureVO[] allVOs = query.queryProcedureByRoots(pkRoots);
        if (ArrayUtils.isEmpty(allVOs)) {
            return new ProcedureVO[0];
        }
        //构造树
        Map<String, List<ProcedureVO>> procedureChildMap = new HashMap<>();
        for (ProcedureVO vo : allVOs) {
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<ProcedureVO> children = procedureChildMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                procedureChildMap.put(parent, children);
            }
            children.add(vo);
        }
        for (ProcedureVO vo : allVOs) {
            List<ProcedureVO> children = procedureChildMap.get(vo.getPkProcedure());
            if (children != null) {
                vo.setChildProcedure(children.toArray(new ProcedureVO[children.size()]));
            }
        }
        List<ProcedureVO> result = procedureChildMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new ProcedureVO[0];
        } else {
            return result.toArray(new ProcedureVO[result.size()]);
        }
    }

    //设置父级code用于前端展示
    private ProcedureVO buildPcode(ProcedureVO vo) throws BusinessException {
        if(vo == null || StringUtils.isBlank(vo.getPkParent())) return vo;

        String parent = vo.getPkParent();
        ProcedureVO pvo = query.getById(parent);
        if(pvo != null) vo.setPcode(pvo.getScode());

        return vo;
    }

    /**
     * 对数据加密
     *
     * @param data
     * @return
     * @throws Exception
     */
    private String encryptData(String data) throws Exception {
        try {
            EncryptUtil encryptUtil = new EncryptUtil(pmcloudServerProperties.getDeSkey(), "utf-8");
            return encryptUtil.encode(data);
        } catch (Exception e) {
            throw new BusinessException("加密失败");
        }
    }




}
